package com.kotlinvben.service

import com.kotlinvben.entity.Role
import com.kotlinvben.enumeration.Status
import com.kotlinvben.exception.RoleNotFoundException
import com.kotlinvben.repository.MenuRepository
import com.kotlinvben.repository.RoleRepository
import com.kotlinvben.utils.SecurityUtils
import com.kotlinvben.vo.RoleUpdateVO
import jakarta.persistence.criteria.Predicate
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.domain.Specification
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class RoleService(
    private val roleRepository: RoleRepository,
    private val menuRepository: MenuRepository
) {
    fun getRoleById(id: Long): Role? {
        return roleRepository.findById(id).orElse(null)
    }

    fun getRoles(
        pageable: Pageable,
        name: String? = null,
        status: Int? = null
    ): Page<Role> {
        val spec = Specification<Role> { root, _, cb ->
            val predicates = mutableListOf<Predicate>()

            name?.let {
                predicates.add(cb.like(root.get("name"), "%$it%"))
            }

            status?.let {
                predicates.add(cb.equal(root.get<Int>("status"), Status.ENABLED.code))
            }

            if (predicates.isEmpty()) null else cb.and(*predicates.toTypedArray())
        }
        val roles = roleRepository.findAll(spec, pageable)
        roles.map { role -> role.permissions = role.menus.map { it.id!! } }
        return roles
    }

    fun createRole(role: Role, permissions: List<Long>?): Role {
        permissions?.let { ids ->
            val menus = menuRepository.findAllById(ids)
            role.menus = menus
        }
        return roleRepository.save(role)
    }

    fun updateRole(id: Long, updateVO: RoleUpdateVO): Role {
        val role = getRoleById(id) ?: throw RoleNotFoundException()
        role.name = updateVO.name
        role.code = updateVO.code
        role.status = updateVO.status
        role.remark = updateVO.remark
        role.modifyBy = SecurityUtils.getCurrentUsername()

        updateVO.permissions?.let { ids ->
            val menus = menuRepository.findAllById(ids)
            role.menus = menus
        }

        return roleRepository.save(role)
    }

    fun deleteRole(id: Long) {
        roleRepository.deleteById(id)
    }

    fun updateRoleStatus(id: Long, status: Int): Role {
        val role = getRoleById(id) ?: throw RoleNotFoundException()
        role.status = status
        role.modifyBy = SecurityUtils.getCurrentUsername()
        return roleRepository.save(role)
    }

    fun getAssignableRoles(): List<Role> {
        val spec = Specification<Role> { root, _, cb ->
            val predicates = mutableListOf<Predicate>()

            // 排除管理员角色
            predicates.add(cb.notEqual(root.get<String>("code"), "super"))
            // 只返回启用的角色
            predicates.add(cb.equal(root.get<Int>("status"), Status.ENABLED.code))

            cb.and(*predicates.toTypedArray())
        }
        val roles = roleRepository.findAll(spec)
        roles.map { role -> role.permissions = role.menus.map { it.id!! } }
        return roles
    }
} 